﻿using System;
using System.Diagnostics;

using Datagility.Shpt.SPDocMirror.Settings;

using Microsoft.SharePoint;

namespace Datagility.Shpt.SPDocMirror.Service
{
    /// <summary>
    /// Processes SharePoint Audit Log entries, mirroring each change to a document in a 'shadow folder' 
    /// on the file system.
    /// </summary>
    public class Processor: IDisposable
    {
        private SPSite siteCollection = null;
        private AuditEntryCollection auditEntries = null;

        public Processor(SPSite SiteCollection)
        {
            if (SiteCollection == null)
            {
                throw new ArgumentException("Parameter Cannot be Null", "SiteCollection");
            }

            siteCollection = SiteCollection;
        }

        public Processor(string SiteUrl)
        {
            if (SiteUrl == null)
            {
                throw new ArgumentException("Parameter Cannot be Null", "SiteUrl");
            }

            siteCollection = new SPSite(SiteUrl);
        }

        /// <summary>
        /// Queries the Audit Log for entries logged after a specified time and for each entry 
        /// replays that change in a mirror folder under the specified 'shadow' root.
        /// </summary>
        /// <param name="ReadFromTime">The earliest log entry to read. This time must be in UTC format.</param>
        /// <param name="ShadowRoot">A string representing the path of the the root 'Shadow' folder.</param>
        /// <returns></returns>
        public int ProcessChanges(MirrorJobSettings settings)
        {
            SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Starting...");

            int fileCount = 0;
            AuditLog log = new AuditLog(siteCollection);

            //Make sure we get all entries between the specified earliest time and now
            DateTime earliestRead = settings.LastRunStart;
            DateTime lastRead = DateTime.UtcNow;

            auditEntries = log.GetChanges(earliestRead, lastRead, settings.LibrariesEnabled);
            SPMirrorLoggingService.Write("Information", String.Format("Processor.ProcessChanges Queried Audit Log - Got {0} Entries",
                auditEntries.Count.ToString()));

            foreach (AuditEntry entry in auditEntries)
            {
                //Process each entry in turn - note that we cannot make assumptions as to the order in which
                // changes are logged to the Audit Log
                switch (entry.Event)
                {
                    case(AuditEventType.Update):
                        SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Processing Entry - Update");

                        if (!entry.DocLocation.StartsWith("Long Running Operation"))
                        {
                            //New or updated Items
                            DoAddFile(siteCollection, entry, settings.ShadowRoot);
                            fileCount++;
                        }
                        break;
                    case(AuditEventType.Delete):
                        SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Processing Entry - Delete");

                        //Deleted Items
                        // delete the document from the shadow folder
                        ShadowFolder fldr = null;
                        fldr = new ShadowFolder(settings.ShadowRoot, entry.DocFolder);
                        fldr.DeleteFile(entry.DocFileName, false);
                        fileCount++;
                        break;
                    case(AuditEventType.Undelete):
                        SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Processing Entry - Undelete");

                        //Add the newly restored item
                        DoAddFile(siteCollection, entry, settings.ShadowRoot);
                        fileCount++;
                        break;
                    case(AuditEventType.Move):
                        SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Processing Entry - Move");

                        //Get the new document location and treat the operation as
                        // an update
                        string docLocation = entry.EventData.Replace(@"<NewName>", @"");
                        docLocation = docLocation.Replace(@"</NewName>", @"");
                        entry.DocLocation = docLocation;
                        DoAddFile(siteCollection, entry, settings.ShadowRoot);
                        fileCount++;
                        break;
                }
            }
            SPMirrorLoggingService.Write("Information", "Processor.ProcessChanges Complete");

            return fileCount;
        }

        private void DoAddFile(SPSite siteCollection, AuditEntry entry, string shadowRoot)
        {
            SPMirrorLoggingService.Write("Information", "Processor.DoAddFile Starting...");

            ShadowFolder fldr = null;

            // get the document
            DocLibrary docLib = new DocLibrary(siteCollection, entry.WebId, entry.ListId);
            byte[] doc = docLib.GetDocument(entry.DocFileName);

            // delete it from the shadow folder if it already exists.
            fldr = new ShadowFolder(shadowRoot, docLib.Url);
            fldr.DeleteFile(entry.DocFileName, false);

            //In the case of renaming a document we may not have found it in the doc lib
            if (doc != null)
            {
                // write it to the shadow folder
                fldr.AddFile(entry.DocFileName, doc);
            }

            SPMirrorLoggingService.Write("Information", "Processor.DoAddFile Complete");
        }

        public void Dispose()
        {
            if (siteCollection != null)
            {
                siteCollection.Dispose();
            }
        }
    }
}
